<?php
// More details at  https://lucidar.me/en/web-dev/server-sent-event-simple-php-example/ 

header('Content-Type: text/event-stream'); // 设置 MIME 类型为：服务器发送事件（SSE）
header('Cache-Control: no-cache');  // 设置 HTTP 响应的 Cache-Control 为 no-cache，告诉浏览器不要缓存此响应

// 设置 HTTP 响应的 Connection 为 keep-alive，保持长连接，以便服务器可以持续发送事件到客户端。
header('Connection: keep-alive');

// 设置 HTTP 响应的自定义头部 X-Accel-Buffering 为 no，
//   用于禁用某些代理或 Web 服务器（如 Nginx）的缓冲。这有助于确保 SSE 在传输过程中不会受到缓冲影响
header('X-Accel-Buffering: no');

// 禁用 zlib 压缩。通常情况下，启用 zlib 压缩可以减小发送到浏览器的数据量，但对于 SSE 来说，实时性更重要，因此需要禁用压缩。
ini_set('zlib.output_compression', false);


// 关闭输出缓冲。关闭后，脚本的输出将立即发送到浏览器，而不是等待缓冲区填满或脚本执行完毕。
//   这样设置是否意味着可以不用在代码中添加 ob_flush() ？事实并非如此。
// ini_set('output_buffering', 'off');

// 这行代码使用循环来清空所有当前激活的输出缓冲区。
//   ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区，@ 符号用于抑制可能出现的错误或警告。
//while (@ob_end_flush()) ;

// Clean (erase) the output buffer and turn off output buffering
//   这一行设置非常重要，可以在 while 循环中不再使用 `ob_flush()` 了。
ob_end_clean();
//ob_implicit_flush(1);  // Turn implicit flush on/off

//der('X-Accel-Buffering: no'); Loop until the client close the stream
//ob_start();  // 开启输出缓冲

function produceMsg()
{
    $time = date('r');
    echo "data: The GPU server time is: {$time}\n\n"; // 在字符串中使用变量，必须用双引号。
}

function produceTimeEvt()
{
    $json = json_encode(['time' => date('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE);
    // echo $json;
    echo "event: time\ndata: $json".PHP_EOL.PHP_EOL;  // PHP_EOL 相当于 `\n`。
}

function producePingEvt()
{
    $curDate = date(DATE_ISO8601);

    $json = json_encode(['time' => $curDate]);
    echo "event: ping\n",
    "data: $json \n\n";

    // echo "event: ping\n",
    // 'data: {"time": "' . $curDate . '"}', "\n\n";
}

while (true) {
    // Echo time
    produceMsg();
    produceTimeEvt();
    producePingEvt();

    // while (ob_get_level() > 0)
    //     ob_end_flush();  // Flush (send) the output buffer and turn off output buffering

    // 刷新缓冲区，非常重要！否则，客户端半天都不会收到信息
    // ob_flush();
    // ob: output buffer.
    // ob_flush： Flush (send) the output buffer. see: https://www.php.net/manual/en/function.ob-flush.php
    // ob_*系列函数, 是操作PHP本身的输出缓冲区。所以, ob_flush 是刷新PHP自身的缓冲区.

    // Flush buffer (force sending data to client)
    flush();  // flush() 是刷新 WebServer (可以认为特指Apache) 的缓冲区

    // break the loop if the client aborted the connection (closed the page)
    if (connection_aborted()) break;

    // Wait 2 seconds for the next message / event
    sleep(1);
}
